package ru.myrtle.jst.parser;

import java.io.CharArrayWriter;
import java.io.IOException;
import java.io.Reader;
import java.util.List;

import ru.myrtle.jst.store.Template;

class Buffer {

    private String data;
    private int cursor;

    private List<Line> lines;

    /*
     *
     */

    public Buffer() {
    }

    /*
     *
     */

    public boolean reading() {
        return cursor < data.length();
    }

    public char current() {
        return data.charAt(cursor);
    }

    public void advance(int count) {
        cursor += count;
    }

    public void advance() {
        advance(1);
    }

    public Mark mark() {
        return new MarkImpl(cursor);
    }

    public void reset(Mark mark) {
        cursor = mark.getPosition();
    }

    public String getString(int start, int end) {
        return data.substring(start, end);
    }

    /*
     *
     */

    /**
     * Read the contents of the input stream into a char array.
     */
    public void fill(Template template) throws IOException {
        char buf[] = new char[1024];

        Reader reader = template.getContentReader();
        try {
            CharArrayWriter writer = new CharArrayWriter();
            try {
                for (int i = 0; (i = reader.read(buf)) != -1;) {

                    //
                    // Remove zero width non-breaking space, which may be used as a byte
                    // order marker,
                    // and may be ignored according to the Unicode FAQ:
                    // http://www.unicode.org/unicode/faq/utf_bom.html#38
                    //

                    if (buf[0] == '\uFEFF') {
                        writer.write(buf, 1, i - 1);
                    }
                    else {
                        writer.write(buf, 0, i);
                    }
                }

                this.data = writer.toString();
            }
            finally {
                writer.close();
            }
        }
        finally {
            reader.close();
        }
    }

    /*
     *
     */

    List<Line> getLines() {
        if (lines == null) {
            lines = Line.buildLineList(data);
        }
        return lines;
    }

    Line getLineForOffset(int offset) {
        for (Line line : getLines()) {
            if (offset >= line.getStart() && offset <= line.getEnd()) {
                return line;
            }
        }

        return null;
    }

    /*
     *
     */

    final class MarkImpl implements Mark {

        private final int position;

        public MarkImpl(int position) {
            this.position = position;
        }

        @Override
        public int getPosition() {
            return position;
        }

        private int line = -1;
        private int column = -1;

        void initLineInfo() {
            if (line == -1 || column == -1) {
                Line line = getLineForOffset(position);

                this.line = line.getNumber();
                this.column = line.getColumn(position);
            }
        }

        @Override
        public int getLine() {
            initLineInfo();
            return line;
        }

        @Override
        public int getColumn() {
            initLineInfo();
            return column;
        }
    }
}
